//
// Created by 郝羽 on 2017/11/21.
//

#ifndef RTC_XSWITCH2_PACKET_HISTORY_HPP
#define RTC_XSWITCH2_PACKET_HISTORY_HPP

#include <cstdint>
#include <functional>
#include <iomanip>
#include <iostream>


class PacketInfo {
public:
    uint16_t seq;
    uint16_t bytes;
    int64_t send_timestamp_ms;
    int64_t recv_timestamp_ms;

};

template <size_t MaxPackets>
class PacketHistory {
    //static constexpr size_t MaxPackets = 128;
public:
    void insert_packet(uint16_t seq, int16_t bytes, int64_t send_ts, int64_t recv_ts) {
        PacketInfo p;
        p.seq = seq;
        p.bytes = bytes;
        p.send_timestamp_ms = send_ts;
        p.recv_timestamp_ms = recv_ts;


        if (_is_empty) {
            _last = 0;
            _packets[0] = p;
            _is_empty = false;
            return;
        }

        uint16_t last_seq = _packets[_last].seq;
        uint16_t first_seq = last_seq - MaxPackets + 1;
        uint16_t d1 = seq - last_seq;
        uint16_t d2 = last_seq - seq;

        if (d1 < 3000) {
            _last += d1;
            _last %= MaxPackets;
            _packets[_last] = p;
            //std::cout << "insert seq " << seq << " send ts " << send_ts << " at " << _last << " of " << first_seq << "-" << last_seq << std::endl;
        } else if (d2 < MaxPackets) {
            int pos = (_last-d2);
            if (pos < 0) {
                pos += MaxPackets;
            }
            PacketInfo& packet = _packets[pos];
            if (packet.seq == seq) {
                //std::cout << "insert seq " << " send ts " << send_ts << " already exist send ts " << packet.send_timestamp_ms << std::endl;
                return;
            }
            _packets[pos] = p;
            //std::cout << "insert seq " << seq << " send ts " << send_ts << " at " << pos << " of " << first_seq << "-" << last_seq << std::endl;
        } else {
            //std::cout << "insert seq " << seq << " send ts " << send_ts <<  " out of window " << first_seq << "-" << last_seq << std::endl;
        }

    };

    bool get(PacketInfo& p, uint16_t seq) {
        if (_is_empty) {
            return false;
        }
        uint16_t last_seq = _packets[_last].seq;

        uint16_t d2 = last_seq - seq;
        if (d2 >= MaxPackets) {
            return false;
        }
        int pos = _last - d2;
        if (pos < 0) {
            pos += MaxPackets;
        }

        PacketInfo& packet = _packets[pos];
        if (packet.seq != seq) {
            return false;
        }

        p = packet;
        return true;
    }

    void for_each(std::function<void(const PacketInfo& info, bool is_last)> func) {
        if (_is_empty) {
            return;
        }
        int pos = _last - MaxPackets + 1;
        if (pos < 0) {
            pos += MaxPackets;
        }
        uint16_t seq = _packets[_last].seq - MaxPackets + 1;
        while (true) {
            PacketInfo& info = _packets[pos];
            if (info.seq == seq++) {
                func(info, pos == _last);
            }
            if (pos == _last) {
                break;
            }
            ++pos;
            pos %= MaxPackets;
        }
    };

    void reset() {
        memset(_packets, 0, sizeof(_packets) / sizeof(PacketInfo));
        _last = 0;
        _is_empty = true;
    };

private:
    bool                        _is_empty = true;
    PacketInfo                  _packets[MaxPackets];
    int                         _last;
};

#endif //RTC_XSWITCH2_PACKET_HISTORY_HPP
